package com.aivin.maproutelib.area;


import com.aivin.maproutelib.bean.WaypointInfo;
import com.aivin.maproutelib.line.ScanLine;
import com.aivin.maproutelib.tool.MapTool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 需要规划的地块
 */
public class AreaMassif {

    /**
     * 地块中的 障碍物列表
     */
    private final List<AreaObstacle> mAreaObstacles=new ArrayList<>();;
    /**
     * 喷幅
     */
    public double len = -1;

    /**
     * 整个地块的边界点 (用户手动点击的那些点组成的圈)
     */
    public final List<WaypointInfo> boundaryLatLngList = new ArrayList<>();

    /**
     * 地块边的边界线，例如四边形地块，共有4条线
     */
    private List<ScanLine> boundarylines = new ArrayList<>();;

    /**
     * 当前 scanLine 与该地块所有边线的 交点
     */
    public List<WaypointInfo> scanLineInterPoints =new ArrayList<>();;


    public AreaMassif(WaypointInfo... points) {
        boundaryLatLngList.addAll(Arrays.asList(points));

        for (int i = 0; i < boundaryLatLngList.size(); i++) {
            WaypointInfo start = boundaryLatLngList.get(i);
            boolean isTheLastPoint = (i == boundaryLatLngList.size() - 1);
            WaypointInfo end= isTheLastPoint ? boundaryLatLngList.get(0) : boundaryLatLngList.get(i + 1);
            ScanLine straightLine = new ScanLine(start, end);
            boundarylines.add(straightLine);
        }
    }


    /**
     * 地块添加障碍物，可以添加多个
     */
    public void addObstacl(AreaObstacle areaObstacle) {
        if (!mAreaObstacles.contains(areaObstacle)) {
            mAreaObstacles.add(areaObstacle);
        }
    }



    /**
     * 获得当前 scanLine 与该地块所有边线的 交点
     */
    public List<WaypointInfo> getThInterPointOfScanLineAndBoundrary(ScanLine scanLine) {
        List<WaypointInfo> list =new ArrayList<>();

        for (ScanLine borderLine : boundarylines) {
            WaypointInfo intersectionPoint = MapTool.getIntersectionPoint(borderLine ,scanLine);
            if (intersectionPoint != null) {
                list.add(intersectionPoint);
            }
        }
        return  list;
    }




    /**
     * 用当前扫描线 b=xx ，并且判断是否要已经扫描完了整个地块
     */
    public boolean isScanFinished(int interPointSize ,List<WaypointInfo> route) {

        if (interPointSize >= 2) {
            WaypointInfo startPoint = route.get(route.size() - 1);//拿到当前位置
            getPointsByCurrentScanline(startPoint, route); // 初次调用
            return false;
        }else{
            // 如果交点数小于2，说明扫描已经完成，需要退出
            return true;
        }
    }

    /**
     * 获得当前扫描线 与 地块的两个交点。也就两个航点。 加入航点队列
     */
    private void getPointsByCurrentScanline(WaypointInfo startPoint, List<WaypointInfo> cRoute) {
        double minDis = -1;
        WaypointInfo minFP = null;

        //通过最小距离找到下一个点
        for (WaypointInfo point : scanLineInterPoints) {
            double distance = MapTool.calculationTwoPointDistance(startPoint, point);
            if (minDis == -1 || minDis > distance) {
                minDis = distance;
                minFP = point;
            }
        }

        if (minFP != null) {
            // 一条线与地块有n个交点，每循环一次就移除一个交点，直至全部加入航点
            scanLineInterPoints.remove(minFP);
            checkObstacle(cRoute, minFP);
            cRoute.add(minFP);
            getPointsByCurrentScanline(minFP, cRoute); // 递归
        }
    }


    /**
     *
     * @param route 航点容器
     * @param endPoint  将要加为航点的 那个交点
     */
    private void checkObstacle(List<WaypointInfo> route, WaypointInfo endPoint) {
        WaypointInfo lastPoint = route.get(route.size() - 1);
        ScanLine obstacleScanLineTo = new ScanLine( lastPoint, endPoint);

        sortObstacle(lastPoint);   // 排序

        for (AreaObstacle areaObstacle : mAreaObstacles) {
            areaObstacle.isScanFinished(obstacleScanLineTo , route);
        }
    }





    /**
     * 根据障碍物与航线容器中最后一个航点距离， <br>
     * todo 将障碍物排序 。<br>
     * 一个扫描线可能会经过多个障碍物区域。<br>
     * 所以需要排序。
     * @param  latestFlyPoint 航点容器中 最后那个航点
     */
    private void sortObstacle(WaypointInfo latestFlyPoint) {

        for (AreaObstacle areaObstacle : mAreaObstacles) {
            if (areaObstacle.intersectionPointsOfScanLineAndObstacleArea.size() == 2) {
                double distance0 = MapTool.calculationTwoPointDistance(areaObstacle.intersectionPointsOfScanLineAndObstacleArea.get(0), latestFlyPoint);
                double distance1 = MapTool.calculationTwoPointDistance(areaObstacle.intersectionPointsOfScanLineAndObstacleArea.get(1), latestFlyPoint);
                areaObstacle.distanceToPoint = distance0 > distance1 ? distance1 : distance0;
            }
        }

        Collections.sort(mAreaObstacles, new Comparator<AreaObstacle>() {
            @Override
            public int compare(AreaObstacle lhs, AreaObstacle rhs) {
                if (lhs.distanceToPoint > rhs.distanceToPoint) {
                    return 1;
                } else {
                    return -1;
                }
            }
        });
    }




    /**
     * 以某条直线扫描本地块 最大的扫描距离（deltaB 的变化量）
     */
    public double getMaxDeltaB(WaypointInfo start, float direction) {
        List<ScanLine> scanLines = new ArrayList<>();
        for (WaypointInfo mPoint : boundaryLatLngList) {
            scanLines.add(new ScanLine(mPoint, direction));
        }

        ScanLine first = new ScanLine(start, direction);
        double maxDeltaB = 0;

        for (ScanLine scanLine : scanLines) {
            double absB = Math.abs(first.b - scanLine.b);
            maxDeltaB = absB > maxDeltaB ? absB : maxDeltaB;
        }
        return maxDeltaB;
    }



}
